package com.koron.car.web.applycar.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.bean.workflow.RedirectQuery;
import com.koron.bean.workflow.WorkflowUser;
import com.koron.car.config.Constants;
import com.koron.car.web.applycar.bean.entity.CarApply;
import com.koron.car.web.applycar.bean.entity.CarEntourage;
import com.koron.car.web.applycar.bean.entity.DriverRecord;
import com.koron.car.web.applycar.bean.entity.DriverSchedule;
import com.koron.car.web.applycar.bean.query.CarApplyQuery;
import com.koron.car.web.applycar.bean.query.LastScheduleQuery;
import com.koron.car.web.applycar.bean.vo.CarApplyVO;
import com.koron.car.web.applycar.filter.CarApplyFilter;
import com.koron.car.web.applycar.mapper.CarApplyMapper;
import com.koron.car.web.applycar.mapper.DriverRecordMapper;
import com.koron.car.web.applycar.service.CarApplyService;
import com.koron.car.web.applycar.service.CarEntourageService;
import com.koron.car.web.applycar.service.DriverScheduleService;
import com.koron.car.web.carledger.service.CarService;
import com.koron.car.web.common.CommonUtils;
import com.koron.car.web.driver.service.DriverService;
import com.koron.car.web.motorcade.bean.Motorcade;
import com.koron.car.web.motorcade.mapper.MotorcadeMapper;
import com.koron.car.web.motorcade.service.PrimaryOrgService;
import com.koron.common.core.business.workflow.templates.WorkFlowHandler;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.common.core.util.CodeTools;
import com.koron.common.core.util.message.MessageUtil;
import com.koron.common.core.util.sms.IMobileMessageSender;
import com.koron.common.core.util.sms.MobileMessageVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * (CarApply)表服务实现类
 *
 * @author twcao
 * @since 2022-03-09 10:37:07
 */
@Slf4j
@Service
public class CarApplyServiceImpl implements CarApplyService {

    @Autowired
    private CarEntourageService entourageService;

    @Autowired
    private DriverScheduleService scheduleService;

    @Autowired
    private CarService carService;

    @Autowired
    private DriverService driverService;

    @Autowired
    private PrimaryOrgService primaryOrgService;

    @Autowired
    private IMobileMessageSender iMobileMessageSender;

    @Autowired
    private MessageUtil messageUtil;

    @Autowired
    private CarApplyFilter carApplyFilter;

    @Autowired
    private WorkFlowHandler workflowhandler;

    private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
            2,
            60,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(5),
            new CustomizableThreadFactory("carApply-sendMessage-"),
            new ThreadPoolExecutor.DiscardOldestPolicy());

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    @TaskAnnotation("queryById")
    public CarApplyVO queryById(SessionFactory factory, String id) {
        CarApplyMapper mapper = factory.getMapper(CarApplyMapper.class);
        CarApplyVO apply = mapper.queryById(id);
        MotorcadeMapper motorcadeMapper = factory.getMapper(MotorcadeMapper.class);
        Motorcade motorcade = motorcadeMapper.queryByName(apply.getApplyMotorcadeName());
        apply.setApplyMotorcadeDeptName(motorcade != null ? motorcade.getDeptName() : "");
        configWorkflow(factory, apply);
        extract(apply);
        return apply;
    }

    /**
     * 新增数据
     *
     * @param apply 实例对象
     * @return 实例对象
     */
    @Override
    @TaskAnnotation("insert")
    public CarApplyVO insert(SessionFactory factory, CarApplyVO apply) {
        CarApplyMapper mapper = factory.getMapper(CarApplyMapper.class);
        // 设置初试状态
        apply.setState(Constants.ApplyState.DRAFT);
        EamUser eamUser = ThreadLocalContext.get();
        // 设置创建、更新信息
        CommonUtils.update(apply, eamUser);
        // 默认未删除
        CommonUtils.update(apply);
        // 设置主键
        apply.setId(CodeTools.getCode32());
        apply.setCode(CommonUtils.getCodeNo(Constants.CAR_APPLY_CODE, 3));
        apply.setDeptId(eamUser.getOrgId());
        merge(apply);
        List<CarEntourage> entourages = apply.getEntourages();
        if (!CollectionUtils.isEmpty(entourages)) {
            entourages.stream().forEach(row -> row.setApplyId(apply.getId()));
            // 添加随行人员
            entourageService.insertBatch(factory, entourages);
        }
        // 1 表示记录类型为补登
        if (apply.getRecordType() == 1) {
            apply.setState(Constants.ApplyState.FINISH);
        }
        mapper.insert(apply);
        return queryById(factory, apply.getId());
    }

    /**
     * 修改数据
     *
     * @param apply 实例对象
     * @return 实例对象
     */
    @Override
    @TaskAnnotation("update")
    public CarApplyVO update(SessionFactory factory, CarApplyVO apply) {
        CarApplyMapper mapper = factory.getMapper(CarApplyMapper.class);
        // 不允许更新状态
        apply.setState(null);
        // 设置创建、更新信息
        CommonUtils.update(apply, ThreadLocalContext.get());
        merge(apply);
        // 1 表示记录类型为补登
        if (apply.getRecordType() == 1) {
            apply.setState(Constants.ApplyState.FINISH);
        }
        mapper.update(apply);
        if (CollectionUtils.isNotEmpty(apply.getEntourages())) {
            entourageService.deleteByApplyId(factory, apply.getId());
            List<CarEntourage> entourages = apply.getEntourages();
            entourages.stream().forEach(row -> row.setApplyId(apply.getId()));
            entourageService.insertBatch(factory, entourages);
        }
        return queryById(factory, apply.getId());
    }

    /**
     * 提交申请
     *
     * @param factory
     * @param applyId
     * @return
     */
    @Override
    @TaskAnnotation("submit")
    public CarApplyVO submit(SessionFactory factory, String applyId) {
        Assert.hasText(applyId, "用车申请id不能为空");
        CarApplyMapper mapper = factory.getMapper(CarApplyMapper.class);
        CarApplyVO carApply = mapper.queryById(applyId);
        Assert.notNull(carApply, "用车申请不存在");
        // 未提交前的申请状态
        String state = carApply.getState();
        int index = Constants.ApplyState.stateList.indexOf(state);
        Assert.isTrue(index < Constants.ApplyState.stateList.size() - 1, "申请已完成");
        // 获取下一个状态, 只更新状态
        CarApply bean = new CarApply();
        // 提交后的状态
        String currentState = Constants.ApplyState.stateList.get(index + 1);
        switch (currentState) {
            case Constants.ApplyState.WAIT_RETURN:
                break;
            case Constants.ApplyState.DISPATCHED:
                dispatch(factory, carApply);
                break;
            case Constants.ApplyState.FINISH:
                finish(factory, carApply);
                break;
            default:
                break;
        }
        bean.setState(currentState);
        bean.setId(applyId);
        mapper.update(bean);
        return queryById(factory, applyId);
    }

    /**
     * 通过主键删除数据
     *
     * @param applyId 主键
     * @return 是否成功
     */
    @Override
    @TaskAnnotation("deleteById")
    public boolean deleteById(SessionFactory factory, String applyId) {
        CarApplyMapper mapper = factory.getMapper(CarApplyMapper.class);
        int result = mapper.deleteById(applyId);
        workflowhandler.delete(applyId);
        return result > 0;
    }


    /**
     * 用车申请查询条件
     *
     * @param factory
     * @param query
     * @return
     */
    @Override
    @TaskAnnotation("queryList")
    public List<CarApplyVO> queryList(SessionFactory factory, CarApplyQuery query) {
        CarApplyMapper mapper = factory.getMapper(CarApplyMapper.class);
        boolean flag = false;
        if (Objects.nonNull(query.getApplet()) && query.getApplet()){
            EamUser eamUser = ThreadLocalContext.get();
            try {
                Method filter = CarApplyFilter.class.getDeclaredMethod("filter", SessionFactory.class, List.class, List.class, List.class);
                filter.setAccessible(true);
                // 获取当前登录用户部门下的车辆管理员
                List<WorkflowUser> users = (List<WorkflowUser>) filter.invoke(carApplyFilter, factory, null, Arrays.asList("1503933068359110656"), Arrays.asList(eamUser.getDeptId()));
                if (CollectionUtils.isNotEmpty(users)){
                 flag = users.stream().map(WorkflowUser::getCode).anyMatch(code -> code.equals(eamUser.getAccount()));
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            // 如果是车辆管理员则查询当前部门下状态为待审核、待回车、已完成的用车申请
            if (flag){
                query.setStates(Arrays.asList(Constants.ApplyState.WAIT_REVIEW,Constants.ApplyState.WAIT_RETURN,Constants.ApplyState.FINISH));
                query.setApplet(null);
            }else {
                // 如果不是车辆管理员则只能看到自己相关的用车申请订单
                // 规则：当前登录部门下，申请人、调度司机、调度人、回车登记员
                query.setApplicantId(eamUser.getAccount());
                query.setApplicantName(eamUser.getName());
                query.setStates(Arrays.asList(Constants.ApplyState.DRAFT,Constants.ApplyState.WAIT_REVIEW,Constants.ApplyState.WAIT_RETURN));
            }
            query.setDeptId(eamUser.getDeptId());
        }
        return mapper.queryList(query);
    }

    /**
     * 用车申请查询条件
     *
     * @param factory
     * @param query
     * @return
     */
    @Override
    @TaskAnnotation("queryApplyList")
    public List<CarApplyVO> queryApplyList(SessionFactory factory, CarApplyQuery query) {
        query.setCreateBy(ThreadLocalContext.getLoginUser().getAccount());
        CarApplyMapper mapper = factory.getMapper(CarApplyMapper.class);
        return mapper.queryApplyList(query);
    }


    /**
     * 车辆和司机调度
     *
     * @param factory
     * @param apply
     * @return
     */
    @Override
    public boolean dispatch(SessionFactory factory, CarApplyVO apply) {
        String applyId = apply.getId();
        DriverSchedule schedule = scheduleService.getByApplyId(factory, applyId);
        // 去掉租赁车辆字段
        // 如果不是租赁车辆
        String carId = schedule.getCarId();
        carService.updateState(factory, carId, Constants.CarState.OUTSITE);
        // 如果不是租赁司机
        String driverId = schedule.getDriverId();
        driverService.updateState(factory, driverId, Constants.DriverWorkState.OUTSITE);
        return true;
    }

    /**
     * 申请完成
     *
     * @param factory
     * @param apply
     * @return
     */
    @Override
    public boolean finish(SessionFactory factory, CarApplyVO apply) {
        String applyId = apply.getId();
        DriverSchedule schedule = scheduleService.getByApplyId(factory, applyId);
        String carId = schedule.getCarId();
        carService.updateState(factory, carId, Constants.CarState.FREE);
        Double lastEndMileage = 0.0;
        // 更新里程
        if (schedule.getLastEndMileage() != null) {
            // 移动端拿不到上一次结束里程，自己查询，如果为空，则表明是车辆的第一次行驶，没有上次的行驶记录
            DriverSchedule lastSchedule = scheduleService.getLastSchedule(factory, new LastScheduleQuery().setCarId(carId));
            lastEndMileage = Objects.isNull(lastSchedule) ? lastEndMileage : (lastSchedule.getLastEndMileage() == null ? 0.0 : lastSchedule.getLastEndMileage());
        }
        // 本单行驶距离（可能不是实际的，当本单的起始里程不等于上单的结束里程时，currDistance就会比实际的大，以填入的值为主，避免车辆拿去维修、加油时，行驶了部分里程）
        Double currDistance = schedule.getEndMileage() - lastEndMileage;
        Assert.isTrue(currDistance >= 0, "本单结束里程不得小于上单结束里程");
        carService.updateDistance(factory, carId, currDistance, schedule.getEndMileage());
        String driverId = schedule.getDriverId();
        driverService.updateState(factory, driverId, Constants.DriverWorkState.FREE);
        return true;
    }

    /**
     * 工作流更新
     *
     * @param factory
     * @param redirectQuery
     */
    @Override
    @TaskAnnotation("workflowUpdate")
    public Boolean workflowUpdate(SessionFactory factory, RedirectQuery redirectQuery) {
        Integer operation = redirectQuery.getOperation();
        CarApplyVO vo = JSON.parseObject(redirectQuery.getFormVariables(), CarApplyVO.class);
        CarApplyMapper mapper = factory.getMapper(CarApplyMapper.class);
        switch (operation) {
            case 1: // 驳回
                // 驳回的节点编号
                vo.setState(Constants.ApplyState.DRAFT);
                break;
            case 32: // 同意
                vo.setState(Constants.ApplyState.WAIT_REVIEW);
                break;
            default:
                break;
        }
        // 更新状态
        mapper.updateStateById(vo.getId(), vo.getState());
        return true;
    }

    /**
     * 审核完成(状态改为待派车)
     *
     * @param factory
     * @param detail
     * @return
     */
    @Override
    @TaskAnnotation("auditFinish")
    public CarApplyVO auditFinish(SessionFactory factory, CarApplyVO detail) {
        CarApplyMapper mapper = factory.getMapper(CarApplyMapper.class);
        DriverSchedule schedule = scheduleService.getByApplyId(factory, detail.getId());
        Assert.notNull(schedule,"当前调度信息为空！");
        dispatch(factory, detail);
        detail.setState(Constants.ApplyState.WAIT_RETURN);
        mapper.updateStateById(detail.getId(), detail.getState());
        //审核完成之后 流程审批完成，通知司机（以短信方式）/发送信息。并添加申请单号
        sendPhoneMessageToDriver(detail, schedule);
        //审核完成之后 流程审批完成，通知申请人（以短信方式）
        sendPhoneMessageToApplyAccount(detail,schedule);
        return detail;
    }

    private void sendPhoneMessageToApplyAccount(CarApplyVO carApply, DriverSchedule schedule) {
        String messageContent = "【用车通知】：您提交的用车申请已通过，请查收！\n" +
                "申请单号：%s \n" +
                "出发地：%s \n" +
                "目的地：%s \n" +
                "预期开始时间：%s \n" +
                "预期结束时间：%s \n" +
                "车牌号：%s \n" +
                "司机姓名：%s \n" +
                "联系方式：%s";

        messageContent = String.format(messageContent, carApply.getId(),
                carApply.getOrigin(),
                carApply.getDestination(),
                DateUtil.format(carApply.getStartTime(), "yyyy-MM-dd HH:mm"),
                DateUtil.format(carApply.getEndTime(), "yyyy-MM-dd HH:mm"),
                schedule.getLicPlateNum(),
                schedule.getDriverName(),
                schedule.getPhone()
        );

        String finalMessageContent = messageContent;
        threadPoolExecutor.execute(() -> {
            try {
                sendPhoneMessage(carApply.getApplicantPhone(), finalMessageContent);
                log.info("sendPhoneMessageToApplyAccount messageInfo={}", finalMessageContent);
            } catch (Exception e) {
                log.error("sendPhoneMessageToApplyAccount e={}", e.getMessage());
            }
        });
    }

    public void sendPhoneMessageToDriver(CarApplyVO carApply, DriverSchedule schedule) {
        String messageContent = "【派车通知】：您有一条新的派车申请，请查收！\n" +
                "申请单号：%s \n" +
                "申请人：%s \n" +
                "申请人电话：%s \n" +
                "出发地：%s \n" +
                "目的地：%s \n" +
                "预期开始时间：%s \n" +
                "预期结束时间：%s \n" +
                "用车事由：%s \n" +
                "车牌号：%s";

        messageContent = String.format(messageContent,
                carApply.getId(),
                carApply.getApplicantName(),
                carApply.getApplicantPhone(),
                carApply.getOrigin(),
                carApply.getDestination(),
                DateUtil.format(carApply.getStartTime(), "yyyy-MM-dd HH:mm"),
                DateUtil.format(carApply.getEndTime(), "yyyy-MM-dd HH:mm"),
                carApply.getReason(),
                schedule.getLicPlateNum());

        String finalMessageContent = messageContent;
        threadPoolExecutor.execute(() -> {
            try {
                sendPhoneMessage(schedule.getPhone(), finalMessageContent);
                log.info("sendPhoneMessageToDriver messageInfo={}", finalMessageContent);
            } catch (Exception e) {
                log.error("sendPhoneMessageToDriver e={}", e.getMessage());
            }
        });
    }

    /**
     * 根据车牌号或车辆id查询申请信息获取当前车辆对应的所有待回车的申请单
     *
     * @param factory
     * @param licPlateNum
     * @param carId
     * @return
     */
    @Override
    @TaskAnnotation("scanCar")
    public List<CarApplyVO> scanCar(SessionFactory factory, String licPlateNum, String carId) {
        Assert.isTrue(StringUtils.isNotBlank(licPlateNum) || StringUtils.isNotBlank(carId), "车牌号和车辆id不能同时为空");
        CarApplyMapper mapper = factory.getMapper(CarApplyMapper.class);
        DriverRecordMapper recordMapper = factory.getMapper(DriverRecordMapper.class);
        CarApplyQuery query = new CarApplyQuery();
        if (StringUtils.isNotBlank(carId)) {
            // 如果车辆id不为空，以carId为主
            licPlateNum = null;
        }
        query.setCarId(carId);
        query.setLicPlateNum(licPlateNum);
        // 只查已派车状态的申请单
        query.setStates(Arrays.asList(Constants.ApplyState.WAIT_RETURN));
        List<CarApplyVO> applies = mapper.queryList(query);
        if (CollectionUtils.isEmpty(applies)) {
            return null;
        }
        CarApplyVO apply = applies.get(0);
        // 获取当前用户的信息（扫描二维码信息）
        EamUser eamUser = ThreadLocalContext.get();
        DriverRecord drivingRecord = recordMapper.drivingRecord(apply.getId(), eamUser.getCurrLeader());
        apply.setDriverRecord(drivingRecord);
        return applies;
    }

    @Override
    @TaskAnnotation("getDrivingRecord")
    public DriverRecord getDrivingRecord(SessionFactory factory, String applyId) {
        Assert.isTrue(StringUtils.isNotBlank(applyId), "申请订单id不能为空");
        DriverRecordMapper recordMapper = factory.getMapper(DriverRecordMapper.class);
        EamUser eamUser = ThreadLocalContext.get();
        return recordMapper.drivingRecord(applyId, eamUser.getCurrLeader());
    }


    private void configWorkflow(SessionFactory factory, List<CarApplyVO> applys) {
        // 申请人所在部门
        List<String> applicantIds = applys.stream().map(CarApplyVO::getApplicantDeptId).collect(Collectors.toList());
        List<String> primary = primaryOrgService.isPrimary(factory, applicantIds);
        applys.stream().forEach(row -> {
            row.setUsingCarType(row.getApplyCarType());
            row.setPrimary(primary.contains(row.getApplicantDeptId()) ? Constants.Primary.YES : Constants.Primary.NO);
            // usingCarType 默认等于 applyCarType
            if (row.getEndTime().getTime() - row.getStartTime().getTime() >= 3 * 24 * 3600 * 1000) {
                // 如果用车时间超过3天，则算长途，即使选择的是短途用车
                row.setUsingCarType(Constants.UsingCarType.LONG_DISTANCE_INSITE);
            }
        });
    }

    private void configWorkflow(SessionFactory factory, CarApplyVO apply) {
        if (apply == null) {
            return;
        }
        List<String> primary = primaryOrgService.isPrimary(factory, Arrays.asList(apply.getApplicantDeptId()));
        apply.setPrimary(primary.contains(apply.getApplicantDeptId()) ? Constants.Primary.YES : Constants.Primary.NO);
        // usingCarType 默认等于 applyCarType
        apply.setUsingCarType(apply.getApplyCarType());
        if (apply.getEndTime().getTime() - apply.getStartTime().getTime() >= 3 * 24 * 3600 * 1000) {
            // 如果用车时间超过3天，则算长途，即使选择的是短途用车
            apply.setUsingCarType(Constants.UsingCarType.LONG_DISTANCE_INSITE);
        }
    }

    /**
     * 回显时
     *
     * @param apply
     */
    private void extract(CarApplyVO apply) {
        if (Objects.isNull(apply)) {
            return;
        }
        List<CarEntourage> entourages = apply.getEntourages();
        //随行人员中的领导
        List<CarEntourage> leaders = entourages.stream().filter(row -> Constants.YesOrNo.YES.equals(row.getLeader())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(leaders)) {
            apply.setLeader(String.valueOf(Constants.YesOrNo.YES));
            apply.setLeaderId(leaders.stream().map(CarEntourage::getUserId).collect(Collectors.joining(",")));
            apply.setLeaderName(leaders.stream().map(CarEntourage::getUserName).collect(Collectors.joining(",")));
            apply.setEntourages(new ArrayList<>(CollectionUtils.subtract(entourages, leaders)));
        } else {
            apply.setLeader(String.valueOf(Constants.YesOrNo.NO));
        }
        apply.setLeaderArr(apply.getLeaders() != null ? Arrays.asList(apply.getLeaders().split(",")) : new ArrayList<>());
    }

    /**
     * 保存时
     *
     * @param apply
     */
    private void merge(CarApplyVO apply) {
        List<CarEntourage> entourages = apply.getEntourages();
        if (String.valueOf(Constants.YesOrNo.NO).equals(apply.getLeader())) {
            return;
        }
        String[] leaderIdArray = StringUtils.split(apply.getLeaderId(), ",");
        String[] leaderNameArray = StringUtils.split(apply.getLeaderName(), ",");
        List<String> leaderArr = apply.getLeaderArr();
        String leaders = leaderArr == null ? null : String.join(",", apply.getLeaderArr());
        apply.setLeaders(leaders);
        if (Objects.isNull(leaderIdArray) || Objects.isNull(leaderNameArray)) {
            return;
        }
        List<String> leaderIds = Arrays.asList(leaderIdArray);
        List<String> leaderNames = Arrays.asList(leaderNameArray);
        Assert.isTrue(CollectionUtils.size(leaderIds) == CollectionUtils.size(leaderNames), "集合id和名称个数不一致");
        Stream.iterate(0, i -> i + 1).limit(leaderIds.size()).forEach(i ->
            entourages.add(new CarEntourage(apply.getId(), leaderIds.get(i), leaderNames.get(i), Constants.YesOrNo.YES))
        );
        apply.setEntourages(entourages);
    }

    @Override
    public int sendPhoneMessage(String phone, String messageContent) {

        MobileMessageVO message = new MobileMessageVO();
        message.setReceiverPhone(phone);
        message.setMessageContent(messageContent);
        return iMobileMessageSender.sendSingleMessage(message);
    }
}
